package com.link.gateway.core.filter.global;

import com.link.gateway.base.auth.model.Authclient;
import com.link.gateway.base.user.model.User;
import com.link.gateway.core.comproperty.*;
import com.link.gateway.core.redis.redisclient.LinkRedisClient;
import com.link.gateway.core.utils.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * 校验权限
 *
 * @author huanglongjian
 * @date 2019年07月22日
 */
@Component
public class LinkPermissionGlobalFilter implements GlobalFilter, Ordered {
    private static final Logger logger = LogManager.getLogger(LinkPermissionGlobalFilter.class);

    @Resource
    private AntPathMatcher antPathMatcher;

    /**
     * 校验url权限
     *
     * @author huanglongjian
     * @date 2019年07月22日
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //跳过检测
        Object ignoreGlobalFilter = exchange.getAttribute(OtherConstants.ATTRIBUTE_IGNORE_GLOBAL_FILTER);
        if (ignoreGlobalFilter != null && (boolean)ignoreGlobalFilter) {
            return chain.filter(exchange);
        }

        long start = System.currentTimeMillis();

        List<String> noIntercept = exchange.getRequest().getHeaders().get(TokenConstants.NO_INTERCEPT_FLAG_NAME);
        if (noIntercept != null && noIntercept.contains(TokenConstants.NO_INTERCEPT_FLAG_VALUE)) {
            //如果请求头里面包含了不拦截的头信息，则直接略过，不做权限控制
            return chain.filter(exchange);
        }

        //获取得到用户信息
        User user = RunProperties.TEMP_USER.get();

        //若管理员从crm入口进来，则不校验范围
        if (UserUtil.isAdmin()) {
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //校验，如果是不拦截的请求，则直接略过
        if (TokenUtils.checkNoIntercept(request)) {
            return chain.filter(exchange);
        }

        //获取得到当前的url
        String requestUrl = request.getURI().getPath();
        //校验默认的url 如果是默认的URL，则直接通过 Hlj 2018年10月11日
        //返回true表示是可以访问的， 返回false表示不可以访问，返回null表示没有当前url信息，走下一步校验
        Boolean defaultAccessFlag = verifyDefaultPermission(requestUrl);

        if (defaultAccessFlag != null && defaultAccessFlag) {
            return chain.filter(exchange);
        } else if (defaultAccessFlag != null) {
            return ResponseDataUtils.writeWith(InterfaceLogConstants.PERMISSION_OVER , "你没有权限访问该接口", "Y", exchange, HttpStatus.FORBIDDEN);
        }

        String curClientId = RunProperties.CURRENT_CLIENT_ID.get();
        String checkPermFlag = null;
        try {
            Authclient authclient = CacheUtil.getAuthclientCache(curClientId);
            checkPermFlag = authclient.getCheckPermFlag();
        } catch (ExecutionException e) {
            logger.error(e.getMessage());
            return ResponseDataUtils.writeWith(InterfaceLogConstants.NORMAL, e.getMessage(), "Y", exchange, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        //校验安全性开关
        if (!"Y".equals(checkPermFlag)) {
            //如果校验企业，没有设置严格安全模式，则直接略过
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                long end = System.currentTimeMillis();
                FunctionTimeUtil.functionTime("LinkPermissionGlobalFilter", "LinkPermissionGlobalFilter1", start, end, false);
            }));
        }


        //校验职责接口
        if (!verifyPermission(requestUrl, user)) {
            return ResponseDataUtils.writeWith(InterfaceLogConstants.PERMISSION_OVER, "你没有权限访问该接口", "Y", exchange, HttpStatus.FORBIDDEN);
        }

        FunctionTimeUtil.functionTime("LinkPermissionGlobalFilterPre", "LinkPermissionGlobalFilter2", start, System.currentTimeMillis(), false);
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            FunctionTimeUtil.functionTime("LinkPermissionGlobalFilterPost", "LinkPermissionGlobalFilter2", start, System.currentTimeMillis(), false);
        }));
    }





    /**
     * 校验默认的url 如果是默认的URL，则直接通过
     *
     * @author huanglongjian
     * @date 2018/10/11 下午8:32
     */
    private Boolean verifyDefaultPermission(String requestUrl) {
        try {
            Map<String, String> defUrl = CacheUtil.getDefaultUrlFromCache(TokenConstants.ALL_DEFAULT_URL_CACHE_KEY);
            for (String url : defUrl.keySet()) {
                if (antPathMatcher.match(url, requestUrl)) {
                    //如果的值为Y，表示可以访问
                    return "Y".equals(defUrl.get(url));
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * 验证用户是否有某个URL的访问权限，如果为true则有，反之则无。
     *
     * @author huanglongjian
     * @date 2019-07-25 18:34
     */
    private Boolean verifyPermission(String requestUrl, User user) {
        LinkRedisClient jedis = RedisUtil.getJedis();
        try {
            List<Long> dutyIdList;
            String scope = RunProperties.SCOPE.get();
            if ("API".equalsIgnoreCase(scope)) {
                //从缓存里面拿出来当前API的职责列表
                dutyIdList = CacheUtil.getClientDutyCache(RunProperties.CURRENT_CLIENT_ID.get());
            } else {
                //非API模式，从用户里面获取用户职责列表
                dutyIdList = user.getDutyIdList();
            }

            if (dutyIdList == null) {
                return false;
            }

            //声明一个数组存储职责id，然后根据此数组获取对应职责下的url
            for (Long dutyId : dutyIdList) {
                String redisKey = OtherConstants.DUTY_URL_KEY_PEX.concat(scope).concat("_").concat(String.valueOf(dutyId)).toUpperCase();
                List<String> urlsList = CacheUtil.getDutyUrlFromCache(redisKey);
                if (urlsList.contains(requestUrl)) {
                    //先校验直接能够匹配的逻辑
                    return true;
                } else {
                    //如果不能够直接匹配，则可能存在路径中有中括号的情况，对这种再校验一次。
                    for (String url : urlsList) {
                        AntPathMatcher antPathMatcher = new AntPathMatcher();
                        if (antPathMatcher.match(url, requestUrl)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            //释放redis连接
            RedisUtil.returnResource(jedis);
        }
        return false;
    }

    @Override
    public int getOrder() {
        return FilterOrderConstants.PERMISSION_CHECK_ORDER;
    }
}
